(function (window) {
    var PIDEG = Math.PI / 180;
    function Gauge (options) {
        var properties= {
            type: "outer",
            tick_length: 12,
            large_tick_length: 22,
            tick_thickness: 2,
            tick_group_length: 9,
            ticks_groups_begin: 0,
            total_degrees: 220,
            tick_color: "#555962",
            tick_on_color: "#527d98",
            on_color_gradient: null,
            bg_color: null, 
            gauge_scale: 1,
            animation_duration: 2000,
            total_tick: 101,
            show_num: true,
            show_center_num: true,
            center_font_size: 200,
            center_font_color: '#555962',
            cur_score_circle_color: '#555962',
            center_offset: {
                x: 0,
                y: 0
            },
            center_num_font_family: 'HanHei SC, PingFang SC, Helvetica Neue Thin, Helvetica, STHeitiSC-Light, Arial, sans-serif',
            num_gap: 1,
            num_begin: 0,
            num_font_size: 16,
            tickmask_offset: 0,
            num_font_family: 'HanHei SC, PingFang SC, Helvetica Neue Thin, Helvetica, STHeitiSC-Light, Arial, sans-serif',
            circle_radius: 5,
            circle_offset: 0,
            center_text_unit: '分',
            center_text_offset: {
                x: 16,
                y: 8
            }
        };
        this.mergeOptions(properties, options)
        this._canvas = options.canvas;
        this.canvas = document.createElement('canvas');
        this.canvas.width = this._canvas.width;
        this.canvas.height = this._canvas.height;
        this.delatLength = this.large_tick_length - this.tick_length;
        this.context = this.canvas.getContext('2d');
        this._context = this._canvas.getContext('2d');
        this._percent = options.percent || 0;
        this._target_percent = this._percent;
        this.tickmask_offset = this.getTickMarkOffset(this.tickmask_offset)
        this._halfCanvasWidth = this.canvas.width / 2;
        this._halfCanvasHeight = this.canvas.height / 2;
        this._rotation_deg = this.getRotationDeg()
        return this;
    }

    Gauge.prototype.mergeOptions = function(defaultOpt, options) {
        var _this = this;
        this._property_list = Object.keys(defaultOpt);
        this._property_list.forEach(function(key) {
          _this[key] = typeof options[key] === 'undefined' ? defaultOpt[key] : options[key]
        })
    }

    Gauge.prototype.getTickMarkOffset = function() {
        return this.tickmask_offset + this.circle_radius * 2 + this.circle_offset;
    }

    Gauge.prototype.getRotationDeg = function() {
        return this.total_degrees / (this.total_tick - 1) * PIDEG;
    }

    Gauge.prototype.render = function (percent) {
        if (typeof percent !== 'undefined') {
          this._percent = percent
        }
        var canvas = this.canvas;
        var context = this.context;
        context.save(); //save original state of context to that it can be restore after rendering
        this._prepareStage();
        //figure out how many degrees between each tick
        var num_ticks = this.total_tick;
        // 计算一下最初始的角度，也就开始的刻度线相对于水平线的角度
        var starting_deg = (180 - this.total_degrees) / 2;
        // 然后将整个画布旋转到初始角度。从左下角可是绘制
        context.rotate(starting_deg * PIDEG);
        // 首先绘制刻度盘上指示刻度的小圆点
        // this._drawScoreTipCircle(this._halfCanvasWidth - this.circle_radius - 70, this.circle_radius, 0);
        
        // 绘制刻度线，num_ticks 为总刻度线的条数。
        for(var i = 1; i <= num_ticks; i++) {
          //判断是刻度线是应该亮起来还是暗的
          var is_on = (((i - 1) / num_ticks) * 100 < this._percent);
          //判断是长刻度线还是短刻度线 
          var _isLargeTick = this._isLargeTick(i)
          var rect_scale = _isLargeTick ? this.large_tick_scale : 1;
          var tick_length = _isLargeTick ? this.large_tick_length : this.tick_length;
          //获取不同刻度线的颜色
          var color = this._getTickColor(is_on, i, this.colorArray);
          //设置填充色
          context.fillStyle = color;
          // 绘制刻度线，即一个长方形。 由于刻度线分长刻度线和短刻度线，而长刻度线和短刻度线因为长度不同，所以长方形的起点位置不同。

          if(this.type == 'outer'){
                var dis = 25
          }else{
                var dis = 70
          }

          if (_isLargeTick) {
            // 绘制长刻度线，即一个长方形
            context.fillRect(-1*this._halfCanvasWidth + this.circle_radius * 2 + this.circle_offset + dis, -this.tick_thickness/2, tick_length, this.tick_thickness);
            // 对于整数值的刻度线，旁边有一个示数，这里绘制示数
            if (this.show_num) {
              this._drawGaugeNum(tick_length, i);
            }
          } else {
            // 绘制短刻度线
            context.fillRect(-1*this._halfCanvasWidth + this.circle_radius * 2 + this.circle_offset + this.delatLength + dis, -this.tick_thickness/2, tick_length, this.tick_thickness);
          }
          //每画完一条，就旋转一下画布。旋转的角度 = 总的刻度角度 / (刻度条的个数 - 1) * Math.PI / 180
          context.rotate(this._rotation_deg);
        }
        // 全部绘制完之后，把前一个旧的canvas清除掉
        this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);
        // 绘制新的图像
        this._context.drawImage(this.canvas, 0, 0);
        // 每次绘制完成后要回到画布的原始的状态
        context.restore();
    };

    Gauge.prototype.updatePercent = function(percent, options) {
        if (percent - 0.1 < 0) {
            return;
        }
        var _this = this;
        this._target_percent = percent;
        options = options || {};
        var duration = ('animation_duration' in options) ? options.animation_duration : _this.animation_duration;
        if(duration) {
            var lastUpdate = +new Date();
            // 动画开始时已经亮起来的刻度的百分比
            var start = this._percent;
            // 目标百分比
            var end = this._target_percent;
            // 计算一下每毫秒我们需要完成多少百分比，然后后面每次执行时，根据经过了多少时间来计算出应该完成多少，即刻度应该亮起来多少
            var change_per_ms = (end - start)/duration;
            var increasing = change_per_ms > 0 ? 1 : 0;
            // 首先计算一下所有刻度亮起来时应该是什么颜色的，保存到一个数组中。
            // 后面动画过程中直接从数组中取值即可,不需要再次计算。
            this.colorArray = this._gradientColorArray(this.total_tick);
            // 小圆球亮起来的颜色数组
            this.colorArray1 = this._gradientColorArray(101);

            // 更新逻辑
            var update = function () {
                // 通过时间来计算应该完成的百分比
                var now = +new Date();
                var elapsed = now - lastUpdate;
                _this._percent += elapsed*change_per_ms;
                lastUpdate= now;
                //检测一下是否已经达到我们最终的百分比，如果达到了就停止动画；如果没有就继续绘制
                if ((increasing && _this._percent < _this._target_percent) || (!increasing && _this._percent > _this._target_percent)) {
                    _this.render();
                    _this._requestAnimFrame(update);
                }else {
                    _this._percent = _this._target_percent;
                    _this.render();
                }
            };
            _this._requestAnimFrame(update);
        }else {
          _this._percent = percent;
          _this.render();
        }
    };

    // 私有函数
    Gauge.prototype._requestAnimFrame = function (f) {
        var anim = window.requestAnimationFrame
          || window.webkitRequestAnimationFrame
          || window.mozRequestAnimationFrame
          || window.oRequestAnimationFrame
          || window.msRequestAnimationFrame
          || function (callback, element) {
            window.setTimeout(function () {
              callback(+new Date);
            }, 1000 / 60);
          };
        anim(f);
    };

    Gauge.prototype._applyBG = function () {
        var canvas = this.canvas;
        var context = this.context;
        if (this.bg_color) {
          context.save();
          context.fillStyle = this.bg_color;
          context.fillRect(0, 0, canvas.width, canvas.height);
          context.restore();
        }
    };

    Gauge.prototype._prepareStage = function() {
        var canvas = this.canvas;
        var context = this.context;
        //clear canvas
        context.clearRect(0, 0, canvas.width, canvas.height);
        //set background
        this._applyBG();
        //set the center of rotation to the bottom/center of the canvas
        context.translate(this._halfCanvasWidth, this._halfCanvasHeight - this.tick_thickness / 2);
        //set the scale of the gauge (will naturally fill the width of the canvas
        context.scale(this.gauge_scale, this.gauge_scale);
        //draw center big num
        if (this.show_center_num) {
          this._drawCenterNum();
        }
    };

    Gauge.prototype._gradientColorArray = function (num) {
        var colorArray = []
        if (!this.on_color_gradient || !this._target_percent) {
          return
        }

        for(var i = 0; i < this.on_color_gradient.length - 1; i++) {
          var next = this.on_color_gradient[i + 1];
          var cur = this.on_color_gradient[i];
          var colorStep = (next.percent - cur.percent) / 100 * num;
          colorArray = colorArray.concat(new gradientColor(cur.color, next.color, colorStep));
        }
        return colorArray;
    }

    Gauge.prototype._getTickColor = function (is_on, index, colorArray) {
        var _index = index < 1 ? 1 : index;
        if (is_on) {
            if (colorArray && colorArray.length > 0) {
                return colorArray[_index-1];
            } else {
                return this.tick_on_color;
            }
        } else {
            return this.tick_color;
        }
    }

    Gauge.prototype._isLargeTick = function(currentNum) {
        return (currentNum + this.ticks_groups_begin - 1) % (this.tick_group_length + 1) === 0
    };

    Gauge.prototype._drawScoreTipCircle = function (beginX, beginY) {
        var context = this.context;
        var is_on = this._percent > 0.01;
        context.save();
        context.fillStyle = this._getTickColor(is_on, Math.floor(this._percent), this.colorArray1);
        context.rotate(this._percent * this.total_degrees / 100 * PIDEG);
        context.beginPath();
        context.arc(-beginX, -beginY, this.circle_radius, 0, Math.PI*2,true);
        context.closePath();
        context.fill();
        context.restore();
    };

    Gauge.prototype._drawCenterNum = function () {
        var context = this.context;
        var canvas = this.canvas;
        var fillColor = this.center_font_color ? this.center_font_color.split('-') : [this.tick_on_color];
        var centerText = Math.floor(this._percent * ((this.total_tick - 1) * this.num_gap + this.num_begin) / 100);
        var gaugeWidth = this.canvas.width / 2 - this.circle_radius * 2 - this.circle_offset - this.large_tick_length - this.tickmask_offset
        context.save();
        if (fillColor.length === 2) {
          var gradient = context.createLinearGradient(-gaugeWidth, 0, gaugeWidth, 0);
          gradient.addColorStop('0', fillColor[0]);
          gradient.addColorStop('0.7', fillColor[1]);
          gradient.addColorStop('1', fillColor[1]);
          context.fillStyle = gradient;
        } else {
          context.fillStyle = fillColor[0];
        }
        context.font = this.center_font_size + 'px ' + this.center_num_font_family;
        context.textAlign = 'center';
        context.textBaseline = 'middle';
        var centerTextWidth = Math.floor(context.measureText(centerText).width);
        context.fillText(centerText, this.center_offset.x, this.center_offset.y);
        context.font = '30px ' + this.num_font_family;
        context.textAlign = 'center';
        context.textBaseline = 'middle';
        context.fillText(this.center_text_unit, centerTextWidth/2 + this.center_text_offset.x, this.center_text_offset.y);
        context.restore();
    };

    Gauge.prototype._drawGaugeNum = function (tick_length, tickIndex) {
        var canvas = this.canvas;
        var context = this.context;
        // var text = this.num_begin + this.num_gap * tickIndex - 1;
        if(this.type == 'outer'){
            var text = (this.num_begin + this.num_gap * tickIndex - 1 ) -49 + 'h';
        }else{
            var text = (this.num_begin + this.num_gap * tickIndex - 1 ) + 0.975 + 'h';
        }
        
        var textWidth = context.measureText(text).width;
        context.save();
        //set the center of rotation to the text middle
        context.translate(-1 * this._halfCanvasWidth + tick_length + this.circle_radius * 2 + this.circle_offset + this.num_font_size / 2, -this.tick_thickness / 2);
        context.rotate(-90 * PIDEG);
        context.font = this.num_font_size + 'px ' + this.num_font_family;
        context.textAlign ='center';
        context.textBaseline ='middle';
        // context.fillText(text , 0, this.tickmask_offset);
        if(this.type == 'outer'){
            context.fillText(text , 0, this.tickmask_offset - 30); 
        }else{
            context.fillText(text , 0, this.tickmask_offset + 70); 
        }

        context.restore();
    };

    window.Gauge= Gauge;
})(window);

function gradientColor(startColor,endColor,step){
    var startRGB = this.colorRgb(startColor);//转换为rgb数组模式
    var startR = startRGB[0];
    var startG = startRGB[1];
    var startB = startRGB[2];

    var endRGB = this.colorRgb(endColor);
    var endR = endRGB[0];
    var endG = endRGB[1];
    var endB = endRGB[2];

    var sR = (endR-startR)/step;//总差值
    var sG = (endG-startG)/step;
    var sB = (endB-startB)/step;

    var colorArr = [];
    for(var i=0;i<step;i++){
        //计算每一步的hex值 
        var hex = this.colorHex('rgb('+parseInt((sR*i+startR))+','+parseInt((sG*i+startG))+','+parseInt((sB*i+startB))+')');
        colorArr.push(hex);
    }
    return colorArr;
}

// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
gradientColor.prototype.colorRgb = function(sColor){
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    var sColor = sColor.toLowerCase();
    if(sColor && reg.test(sColor)){
        if(sColor.length === 4){
            var sColorNew = "#";
            for(var i=1; i<4; i+=1){
                sColorNew += sColor.slice(i,i+1).concat(sColor.slice(i,i+1));
            }
            sColor = sColorNew;
    }
    //处理六位的颜色值
    var sColorChange = [];
    for(var i=1; i<7; i+=2){
        sColorChange.push(parseInt("0x"+sColor.slice(i,i+2)));
    }
        return sColorChange;
    }else{
        return sColor;
    }
};

// 将rgb表示方式转换为hex表示方式
gradientColor.prototype.colorHex = function(rgb){
    var _this = rgb;
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    if(/^(rgb|RGB)/.test(_this)){
        var aColor = _this.replace(/(?:(|)|rgb|RGB)*/g,"").split(",");
        var strHex = "#";
        for(var i=0; i<aColor.length; i++){
            var hex = Number(aColor[i]).toString(16);
            hex = hex<10 ? 0+''+hex :hex;// 保证每个rgb的值为2位
            if(hex === "0"){
                hex += hex;
            }
            strHex += hex;
        }
        if(strHex.length !== 7){
            strHex = _this;
        }
        return strHex;
    }else if(reg.test(_this)){
        var aNum = _this.replace(/#/,"").split("");
        if(aNum.length === 6){
            return _this;
        }else if(aNum.length === 3){
            var numHex = "#";
            for(var i=0; i<aNum.length; i+=1){
                numHex += (aNum[i]+aNum[i]);
            }
            return numHex;
        }
    }else{
        return _this;
    }
}